/* grammars */

%{ (*header*)

let rec parse_string strng = 
	match strng with
		  ""	-> Gnsyn.Nil
		| _	-> Gnsyn.Application(Gnsyn.Identifier("attach"),[Gnsyn.Char(strng.[0]); (parse_string (String.sub strng 1 ((String.length strng) - 1)))])
%}

/*tokens*/
%token IF THEN ELSE ENDIF GETS COMMA FUN TO HAS DO LAND WITH MATCH MWITH ENDMATCH IFD MATCHD PREV
%token LPAREN RPAREN LBRACK RBRACK SEMICOLON NEG EOF COLON USING SIG
%token ADD SUB MULT DIV MOD EXP EXCLAM
%token EQUAL NEQUAL GT LT GE LE AND OR ATTACH
%token <int> INT
%token <float> REAL
%token <bool> BOOL
%token <string> STRING
%token <string> ID, TYPE, TYPEVAR
%token <char> CHAR
%token NIL, DYNAMIC, TICK, STRING_TYPE

/*associativity and precedence -- later declarations have higher precedence*/
%right GETS HAS TO WITH 
%left DO
%left AND OR
%left EQUAL NEQUAL GT LT GE LE 
%left ADD SUB
%left MULT DIV MOD
%right EXP
%right ATTACH
%left COLON
%nonassoc NEG EXCLAM

%start input
%type <Gnsyn.gnarly> input

%%

input:	 	  EOF							{Gnsyn.Empty}
		| SEMICOLON						{Gnsyn.Empty}
		| error SEMICOLON                                       {Gnsyn.Syntax_Error}
                | expression SEMICOLON					{Gnsyn.Expression($1)}
		| USING STRING SEMICOLON				{Gnsyn.Library($2)}

value:		  REAL							{Gnsyn.Real($1)}
		| INT							{Gnsyn.Integer($1)}
		| BOOL							{Gnsyn.Boolean($1)}
		| CHAR							{Gnsyn.Char($1)}
		| NIL							{Gnsyn.Nil}
		| STRING						{parse_string $1}
		| list							{$1}
		| pair                                                  {$1}
		| lambda						{$1}

identifier:	  ID							{Gnsyn.Identifier($1)}
		| PREV                                                  {Gnsyn.Identifier("$$")}

lambda:		  paramdec TO gtype WITH expression			{Gnsyn.Lambda(fst($1),Gnsyn.Arrow_Type(snd($1),$3),$5)}
		| LPAREN paramdec TO gtype RPAREN WITH expression	{Gnsyn.Lambda(fst($2),Gnsyn.Arrow_Type(snd($2),$4),$7)}
		| paramdec WITH expression				{Gnsyn.Lambda(fst($1),Gnsyn.Arrow_Type(snd($1),Gnsyn.Dynamic_Type),$3)}
		| LPAREN paramdec RPAREN WITH expression		{Gnsyn.Lambda(fst($2),Gnsyn.Arrow_Type(snd($2),Gnsyn.Dynamic_Type),$5)}

pair:             LPAREN expression COMMA expression RPAREN             {Gnsyn.Application(Gnsyn.Identifier("cons"),[$2; $4])}
		| TICK LPAREN expression COMMA expression RPAREN        {Gnsyn.Application(Gnsyn.Identifier("cons"),[$3; $5])}

list: 		  LBRACK listvals RBRACK				{$2}
		| TICK LBRACK listvals RBRACK				{$3}

listvals:	  expression						{Gnsyn.Application(Gnsyn.Identifier("attach"),[$1; Gnsyn.Nil])}
		| expression COMMA listvals				{Gnsyn.Application(Gnsyn.Identifier("attach"),[$1; $3])}

gtype:		  TYPE							{Gnsyn.Basic_Type($1)}
		| STRING_TYPE						{Gnsyn.List_Type(Gnsyn.List(Gnsyn.Basic_Type("Char")))}
		| DYNAMIC						{Gnsyn.Dynamic_Type}
		| LBRACK paramtypes TO gtype RBRACK			{Gnsyn.Arrow_Type($2,$4)}
		| LBRACK gtype MULT gtype RBRACK %prec HAS		{Gnsyn.Product_Type([$2;$4])}  
		| LBRACK gtype RBRACK					{Gnsyn.List_Type(Gnsyn.List($2))}

paramtypes:	  LPAREN RPAREN						{[]}
		| gtype							{$1::[]}
		| gtype MULT paramtypes %prec HAS			{$1::$3}

paramdec:	  LPAREN RPAREN						{([],[])}
		| vardec						{(fst($1)::[],snd($1)::[])}
		| vardec MULT paramdec %prec HAS			{(fst($1)::fst($3),snd($1)::snd($3))}

vardec:		  ID COLON gtype					{($1,$3)}
		| ID							{($1,Gnsyn.Dynamic_Type)}

conditional:	  IF expression THEN expression ENDIF			{Gnsyn.Conditional($2,$4,Gnsyn.Nil,true)}
		| IF expression THEN expression ELSE expression ENDIF	{Gnsyn.Conditional($2,$4,$6,true)}
		| IF expression THEN expression ELSE conditional	{Gnsyn.Conditional($2,$4,$6,true)}
		| IFD expression THEN expression ENDIF			{Gnsyn.Conditional($2,$4,Gnsyn.Nil,false)}
		| IFD expression THEN expression ELSE expression ENDIF	{Gnsyn.Conditional($2,$4,$6,false)}
		| IFD expression THEN expression ELSE conditional	{Gnsyn.Conditional($2,$4,$6,false)}

matching:	  MATCH expression MWITH matches ELSE expression ENDMATCH		
									{Gnsyn.Match($2,$4,$6,true)}
		| MATCH expression MWITH DO matches ELSE expression ENDMATCH		
									{Gnsyn.Match($2,$5,$7,true)}	
		| MATCH expression MWITH matches ELSE WITH expression ENDMATCH		
									{Gnsyn.Match($2,$4,$7,true)}
		| MATCH expression MWITH DO matches ELSE WITH expression ENDMATCH		
									{Gnsyn.Match($2,$5,$8,true)}	
		| MATCHD expression MWITH matches ELSE expression ENDMATCH		
									{Gnsyn.Match($2,$4,$6,false)}
		| MATCHD expression MWITH DO matches ELSE expression ENDMATCH		
									{Gnsyn.Match($2,$5,$7,false)}	
		| MATCHD expression MWITH matches ELSE WITH expression ENDMATCH		
									{Gnsyn.Match($2,$4,$7,false)}
		| MATCHD expression MWITH DO matches ELSE WITH expression ENDMATCH		
									{Gnsyn.Match($2,$5,$8,false)}	

matches:	  matchstate WITH expression				{($1,$3)::[]}
		| matchstate WITH expression DO				{($1,$3)::[]}
		| matchstate WITH expression DO matches			{($1,$3)::$5}

matchstate:	  NIL							{"NIL"::[]}
		| ID ATTACH ID						{[$1; $3]}
		| ID ATTACH matchstate					{$1::$3}

sequence:	  expression DO expression				{Gnsyn.Sequence($1,$3)}

let_term:         let_list COMMA expression                             {Gnsyn.Let($1,$3)}

let_list:         let_body                                              {$1::[]}
		| let_body LAND let_list                                {$1::$3}

let_body:	  vardec GETS expression                		{($1,$3)}
		| ID COLON LBRACK paramdec TO gtype RBRACK GETS expression
									{(($1,Gnsyn.Arrow_Type(snd($4),$6)),
										Gnsyn.Lambda(fst($4),Gnsyn.Arrow_Type(snd($4),$6),$9))}

commit:           commit_list                                           {Gnsyn.Commit($1)}

commit_list:      commit_body                                           {$1::[]}
		| commit_body LAND commit_list                          {$1::$3}

commit_body:	  vardec HAS expression					{($1,$3)}
		| ID COLON LBRACK paramdec TO gtype RBRACK HAS expression
									{(($1,Gnsyn.Arrow_Type(snd($4),$6)),
										Gnsyn.Lambda(fst($4),Gnsyn.Arrow_Type(snd($4),$6),$9))}

application: 	  expression arglist %prec COLON			{Gnsyn.Application($1,$2)}

arglist:	  LPAREN RPAREN						{[]}
		| term							{$1::[]}
		| term arglist						{$1::$2}

expression:	  application						{$1}
		| let_term						{$1}
		| commit						{$1}
		| term							{$1}

term:		  sequence						{$1}
		| binexp						{$1}
		| unexp							{$1}
		| conditional						{$1}
		| matching						{$1}
		| value							{$1}
		| identifier						{$1}
		| LPAREN expression RPAREN				{$2}

binexp:	  	  expression ADD expression				{Gnsyn.Application(Gnsyn.Identifier("add"),[$1; $3])}
		| expression SUB expression				{Gnsyn.Application(Gnsyn.Identifier("sub"),[$1; $3])}
		| expression MULT expression				{Gnsyn.Application(Gnsyn.Identifier("mult"),[$1; $3])}
		| expression DIV expression				{Gnsyn.Application(Gnsyn.Identifier("div"),[$1; $3])}
		| expression MOD expression				{Gnsyn.Application(Gnsyn.Identifier("mod"),[$1; $3])}
		| expression EXP expression				{Gnsyn.Application(Gnsyn.Identifier("exp"),[$1; $3])}
		| expression EQUAL expression				{Gnsyn.Application(Gnsyn.Identifier("eq"),[$1; $3])}
		| expression NEQUAL expression				{Gnsyn.Application(Gnsyn.Identifier("neq"),[$1; $3])}
		| expression GT expression				{Gnsyn.Application(Gnsyn.Identifier("gt"),[$1; $3])}
		| expression LT expression				{Gnsyn.Application(Gnsyn.Identifier("lt"),[$1; $3])}
		| expression GE expression				{Gnsyn.Application(Gnsyn.Identifier("ge"),[$1; $3])}
		| expression LE expression				{Gnsyn.Application(Gnsyn.Identifier("le"),[$1; $3])}
		| expression AND expression				{Gnsyn.Application(Gnsyn.Identifier("and"),[$1; $3])}
		| expression OR expression				{Gnsyn.Application(Gnsyn.Identifier("or"),[$1; $3])}
		| expression ATTACH expression				{Gnsyn.Application(Gnsyn.Identifier("attach"),[$1; $3])}

unexp:		  expression EXCLAM					{Gnsyn.Application(Gnsyn.Identifier("fact"),[$1])}
		| SUB expression %prec NEG				{Gnsyn.Application(Gnsyn.Identifier("neg"),[$2])} 
		| EXCLAM expression					{Gnsyn.Application(Gnsyn.Identifier("not"),[$2])}
